package tacticalLayer.tactics;

import java.util.LinkedList;
import java.util.List;

import org.jdom.Element;

import planninglanguage.conditions.AttackedCondition;
import planninglanguage.conditions.Condition;
import planninglanguage.plans.AttackBOPlan;
import planninglanguage.plans.BasicOperatorPlan;
import planninglanguage.plans.Plan;
import util.XMLWriter;
import base.ProxyBot;
import base.WargusGameState;
import base.WargusStateImporter;
import base.WargusUnit;

/*
 * This tactic simply keeps the unit attacking:
 *  - If it can attack he target from a safe spot it will do it
 *  - If not, it will fight back any attacking unit if the target is not attacking it 
 */

public class AttackTactic extends Tactic {
	int m_state;
	AttackBOPlan m_goal;
	AttackedCondition m_attacked;
	LinkedList<Integer> m_attackingUnits;
	int m_currentTarget;
	
	public AttackTactic(int unitID,AttackBOPlan goal) {
		super(unitID);
		m_state = 0;
		m_goal = goal;
		m_attacked = new AttackedCondition(unitID);
		m_currentTarget = -1;
		m_attackingUnits = new LinkedList<Integer>();
	}
	
	public BasicOperatorPlan getGoal() {
		return m_goal;
	}
	
	/*
	 * Returns the tactic to be executed in the next cycle ("null" means end of tactic)
	 */
	public Tactic cycle(WargusGameState gameState) {

		System.out.println("AttackTactic " + m_unitID + ": State " + m_state);

		switch(m_state) {
		case 0: // Attack the Target:
				m_goal.send();
				m_currentTarget = m_goal.getTargetID();
				m_state = 1;
				m_attacked.reset();

				System.out.println("AttackTactic " + m_unitID + ": attacking " + m_currentTarget);
				return this;
		case 1: // Test if under attack or if the Target has been destroyed:
				{
					WargusUnit unit = gameState.getUnit(m_goal.getUnitID());
					WargusUnit targetUnit = gameState.getUnit(m_goal.getTargetID());
					
					if (targetUnit==null) {
						System.out.println("AttackTactic " + m_unitID + ": Target Destroyed!");
						return new IdleTactic(m_unitID);
					}
					if (m_attacked.test(gameState)) m_state = 3;

					{						
						if (WargusStateImporter.statusToString(unit.getStatus()[0]).equals("idle")) {
							System.out.println("AttackTactic " + m_unitID + ": has been found to be idle! Restarting the Tactic");
							m_state = 0;
						} 
					}
					
					return this;				
				}
		case 3: // Look for units that might be attacking us:
				{
					m_attackingUnits.clear();
					WargusUnit u1 = gameState.getUnit(m_unitID);
					List<WargusUnit> enemyUnits = gameState.getEnemyUnits();		
					for(WargusUnit u2:enemyUnits) {
						if (u1.inRangeOf(u2)) {
							m_attackingUnits.add(new Integer(u2.getUnitID()));
						}
					}
					
					System.out.print("AttackTactic " + m_unitID + ": in range of[ ");
					for(Integer a:m_attackingUnits) System.out.print("" + a + " ");
					System.out.println("]");
				}
				
				if (m_attackingUnits.size()==0) {
					System.out.println("AttackTactic " + m_unitID + ": Not in range of any unit!");
					m_state = 0;
					return this;
				} else {
					if (!m_attackingUnits.contains(m_currentTarget)) {
						m_currentTarget = m_attackingUnits.get(0);
						ProxyBot.getProxyBot().attack(m_unitID, m_currentTarget);
						System.out.println("AttackTactic " + m_unitID + ": changing target to " + m_currentTarget);
						return this;
					} else {
						System.out.println("AttackTactic " + m_unitID + ": The current target (" + m_currentTarget + ") is in the attacking units, so continue the attack!");
						return this;						
					}
				}				
		}
		
		return null;		
	}
	
	public void writeXML(XMLWriter w) {
		w.tag("tactic type=\"attack\"");
		{
			w.tag("unit-id", m_unitID);
			w.tag("plan");
			{
				m_goal.writeXML(w);
			}
			w.tag("/plan");
			w.tag("state", m_state);
			m_attacked.writeXML(w);
			w.tag("units");
			{
				for (Integer i : m_attackingUnits) {
					w.tag("unit", i);
				}
			}
			w.tag("/units");
			w.tag("target", m_currentTarget);
		}
		w.tag("/tactic");
	}

	protected static AttackTactic loadTacticFromXML(Element e) {
		assert e.getName().equals("tactic") :
			"AttackTactic.loadFromXML: Invalid XML Element " +	e.getName();
		assert e.getAttributeValue("type").equals("attack") :
			"AttackTactic.loadFromXML: Invalid Tactic type " +	
			e.getAttributeValue("type");
		
		int unitID = Integer.parseInt(e.getChildText("unit-id"));
		AttackBOPlan plan = (AttackBOPlan) Plan.loadPlanFromXML(
				(Element) e.getChild("plan").getChildren().get(0)); 
		
		AttackTactic t = new AttackTactic(unitID, plan);

		t.m_state = Integer.parseInt(e.getChildText("state"));
		for (Object o : e.getChild("units").getChildren("unit")) {
			Element ue = (Element) o;
			t.m_attackingUnits.add(Integer.parseInt(ue.getText()));
		}
		t.m_currentTarget = Integer.parseInt(e.getChildText("target"));
		t.m_attacked = (AttackedCondition) Condition.loadFromXML(e.getChild("condition"));
		
		return t;
	}
}
